import TSpace.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;

import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

class TupleImplementation extends TupleSpacePOA
{
	private ORB               orb;
	private List<Tuple>       tuples;
	private List<Integer>     received; // poderia ser buffer circular
	private ApplicationServer observer;

	public TupleImplementation()
	{
		tuples = new LinkedList<Tuple>();
		received = new LinkedList<Integer>();
		orb = null;
		observer = null;
	}

	public void addTuple(Tuple tup)
	{
		tuples.add(tup);
	}

	public void setObserver(ApplicationServer observer_val)
	{
		observer = observer_val;
	}

	public void setORB(ORB orb_val)
	{
		orb = orb_val; 
	}

	public synchronized boolean ping()
	{
		return true;
	}

	// implement shutdown() method
	public synchronized void shutdown()
	{
		orb.shutdown(false);
	}

	// implement write method
	public synchronized boolean write (TSpace.TupleRequest aTupleRequest)
	{
		Integer req = new Integer(aTupleRequest.requestNumber);
		
		if (received.contains(req))
		{
			System.out.println("\nwrite already processed, ignored..\n");		
			return true;
		}

		System.out.println("\nTupleSpaceImpl::write\n");

		received.add(req);

		Boolean result = tuples.add(aTupleRequest.tuple);

		if (result == true)
		{
			notifyAll();

			if (observer != null)
				observer.write(aTupleRequest);
		}

		System.out.println("\nexiting of TupleSpaceImpl::read\n");
		System.out.println("\nNumber of tuples:" + tuples.size() + "\n");

		return result;
	}

	// implement read method
	public synchronized TSpace.Tuple read (TSpace.TupleTemplate aTemplate)
	{
		System.out.println("\nTupleSpaceImpl::read\n");

		Tuple tup = matchTuple(aTemplate);

		while(tup == null)
		{
			try
			{
				wait();
				tup = matchTuple(aTemplate);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}    		
		}

		System.out.println("\nexiting of TupleSpaceImpl::read\n");
		System.out.println("\nNumber of tuples:" + tuples.size() + "\n");

		return tup;
	}    

	// implement take method
	public synchronized TSpace.Tuple take (TSpace.TupleTemplate aTemplate)
	{
		Integer req = new Integer(aTemplate.requestNumber);
		
		if (received.contains(req))
		{
			System.out.println("\ntake already processed, ignored..\n");		
			return null;
		}

		System.out.println("\nTupleSpaceImpl::take\n");

		Tuple tup = matchTuple(aTemplate);

		while (tup == null)
		{
			try
			{
				wait();
				tup = matchTuple(aTemplate);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}    		
		}

		received.add(req);

		tuples.remove(tup);

		if (observer != null)
			observer.take(aTemplate);

		System.out.println("\nexiting of TupleSpaceImpl::take\n");
		System.out.println("\nNumber of tuples:" + tuples.size() + "\n");

		return tup;
	}

	// implement readAll method
	public synchronized TSpace.Tuple[] readAll ()
	{
		Tuple[] seq = new Tuple[tuples.size()];
		
		int seq_number = 0;

		for (Tuple tup : tuples)
		{
			seq[seq_number] = tup;
			seq_number++;
		}

		System.out.println("\nTupleSpaceImpl::readAll\n");
		System.out.println("\nNumber of tuples:" + tuples.size() + "\n");

		return seq;
	}    

	/*********/
    
	private String typeCodeToString(TypeCode type)
	{
		// ----- Some types codes
		Any anyTypeNull = orb.create_any();

		Any anyTypeInt = orb.create_any();
		anyTypeInt.insert_long(1);

		Any anyTypeString = orb.create_any();
		anyTypeString.insert_string("hi");

		Any anyTypeLong = orb.create_any();
		anyTypeLong.insert_longlong(1L);

		Any anyTypeChar = orb.create_any();
		anyTypeChar.insert_char('a');

		Any anyTypeShort = orb.create_any();
		anyTypeShort.insert_short((short) 1);
		//~ System.out.println("\nanyTypeInt.type()" + anyTypeInt.type() + "\n");
		// -----

		if (type.equal(anyTypeNull.type())) {
			return TupleTypeMap.NULL;
		}
		if (type.equal(anyTypeInt.type())) {
			return TupleTypeMap.INT;
		}
		if (type.equal(anyTypeString.type())) {
			return TupleTypeMap.STRING;
		}
		if (type.equal(anyTypeLong.type())) {
			return TupleTypeMap.LONG;
		}
		if (type.equal(anyTypeChar.type())) {
			return TupleTypeMap.CHAR;
		}
		if (type.equal(anyTypeShort.type())) {
			return TupleTypeMap.SHORT;
		}
		return "Unknown";
	}

	private Tuple matchTuple(TSpace.TupleTemplate aTemplate)
	{
		for (Tuple tup : tuples)
		{
			String typeE1 = typeCodeToString(tup.e1.type()); 
			String typeE2 = typeCodeToString(tup.e2.type());
			String typeE3 = typeCodeToString(tup.e3.type());

			if (typeE1.equals(aTemplate.Types[0]) &&
			    typeE2.equals(aTemplate.Types[1]) &&
			    typeE3.equals(aTemplate.Types[2])) 								
			{
				if (aTemplate.Values == null)
					return tup;

//				System.out.println("VALUES[0]: " + aTemplate.Values[0]);
//				System.out.println("VALUES[1]: " + aTemplate.Values[1]);
//				System.out.println("VALUES[2]: " + aTemplate.Values[2]);

				if (((aTemplate.Values[0].length() == 0) ||
					   TupleImplementation.anyToString(tup.e1, typeE1).equals(aTemplate.Values[0])) &&
				    ((aTemplate.Values[1].length() == 0) ||
				      TupleImplementation.anyToString(tup.e2, typeE2).equals(aTemplate.Values[1])) &&
				    ((aTemplate.Values[2].length() == 0) ||
				      TupleImplementation.anyToString(tup.e3, typeE3).equals(aTemplate.Values[2])))
				{
					return tup;
				}
			}
		}

    	return null;
	}
	
	/*********/

	public static String anyToString(Any anAny, String typeElement)
	{
		switch (TupleTypeMap.stringToTupleType(typeElement))
		{
			case CHAR:
				char[] array = { anAny.extract_char() };
				return new String(array, 0, 1);
			case INT:
				return Integer.toString(anAny.extract_long());
			case LONG:
				return Long.toString(anAny.extract_longlong());
			case SHORT:
				return Short.toString(anAny.extract_short());
			case STRING:
				return anAny.extract_string();

			default:
				throw new RuntimeException("anyToString: should not enter here!");
		}
	}

	public static java.lang.Object createTupleValue(String tupleValue, String tupleType)
	{
		switch (TupleTypeMap.stringToTupleType(tupleType))
		{
			case NULL:
				return new java.lang.Object();
			case INT:
				return new Integer(Integer.decode(tupleValue));
			case CHAR:
				return new Character((tupleValue.length() >= 1 ? tupleValue.charAt(0) : ' '));
			case LONG:
				return new Long(Long.decode(tupleValue));			
			case SHORT:
				return new Short(Short.decode(tupleValue));
			case STRING:
				return tupleValue;
		}
		
		return new java.lang.Object();
	}
}

